﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CLUBsInterpreter.ObjectTypes;
using System.IO;
using CLUBsInterpreter.AST;
using CLUBsInterpreter.Visual;
using System.Drawing;

namespace CLUBsInterpreter
{
    class Interpreter
    {

		private CLUBsNode m_ast;
		private StandardEnvironment m_stdenv;
		private UserInterface m_gui;
		private TextWriter m_errOut;

		public static UserInterface GUI;
		public const int WINDOW_WIDTH = 600;
		public const int WINDOW_HEIGHT = 600;

		public uint Errors
		{
			get;
			private set;
		}

		public Interpreter(TextWriter errOut, CLUBsNode ast)
		{
			m_errOut = errOut;
			Errors = 0;
			m_ast = ast;
			m_stdenv = StandardEnvironment.Instance;
		}

		public void ExecutionError(string msg)
		{
			// Increment amount of errors
			Errors++;
			// Print error
			m_errOut.WriteLine("Execution error: " + msg);
		}

		public void StartGame()
		{
			// If no board has been set, try to find one
			if (m_stdenv.GlobalVariables["CurrentBoard"].LocalVariables.ContainsKey("NullBoard"))
			{
				CLUBsBoard board = null;
				foreach (CLUBsObject obj in m_stdenv.GlobalVariables.Values)
				{
					try
					{
						board = (CLUBsBoard)obj;
						if (board.Grid != null)
							break;
					}
					catch
					{
						// Ignore -- this was not a board
					}

				}

				// TODO: Check in libraries

				if (board != null)
					m_stdenv.GlobalVariables["CurrentBoard"] = board;
			}

			// Call 'start' action
			m_stdenv.CallAction(m_ast.StartAction.Action);

			// Check if the current board is null
			if (((CLUBsBoard)m_stdenv.GlobalVariables["CurrentBoard"]).LocalVariables.ContainsKey("NullBoard"))
			{
				// If so, report error and abort
				ExecutionError("Invalid board");
				return;
			}

			// Initialize GUI
			m_gui = new UserInterface(new Size(WINDOW_WIDTH,WINDOW_HEIGHT));
			GUI = m_gui;
			m_gui.Show();

			// Set the current board to be painted
			m_gui.AddPaintable((CLUBsBoard)m_stdenv.GlobalVariables["CurrentBoard"]);
			m_gui.AddClickable((CLUBsBoard)m_stdenv.GlobalVariables["CurrentBoard"]);

			// Do rounds until game ends
			Delegate[] roundInvocations = m_stdenv.GlobalVariables["CurrentRound"].TranslatedMethod.GetInvocationList();
			Action currentInvocation;
			int i = 0;
			while (!(CLUBsBoolean)m_stdenv.GlobalVariables["GameHasEnded"])
			{
				if (i == roundInvocations.Length)
				{
					i = 0;
				}

				currentInvocation = (Action)roundInvocations[i];
				//roundInvocations[i].DynamicInvoke();
				currentInvocation();

				i++;
			}

			// Deinitialize GUI
			m_gui.Refresh();
			//m_gui.Close();

			// Find winner
			List<CLUBsPlayer> winners = new List<CLUBsPlayer>();
			foreach (CLUBsObject obj in m_stdenv.GlobalVariables.Values)
			{
				try
				{
					CLUBsPlayer player = (CLUBsPlayer)obj;
					if ((CLUBsBoolean)player.LocalVariables["HasWon"] && !winners.Contains(player))
					{
						winners.Add(player);
					}
				}
				catch (InvalidCastException)
				{

				}
			}

			// Print win information
			if (winners.Count == 0)
			{
				Console.WriteLine("The game was a tie.");
			}
			else if (winners.Count == 1)
			{
				Console.WriteLine(winners[0].Name + " won!");
			}
			else
			{
				Console.WriteLine("Multiple winners:");
				foreach (CLUBsPlayer p in winners)
				{
					Console.WriteLine(winners[0].Name);
				}
			}

			Console.WriteLine("Game Over. Thank you for playing.");
		}

    }
}
